use rand::{thread_rng, Rng};
use salvo::prelude::*;
use tokio::{join, time::Instant};
use tracing::info;

#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

struct Fortune {
    fortunes: Vec<String>,
}

#[handler]
impl Fortune {
    async fn new(path: &str) -> Self {
        use tokio::fs::read_to_string;
        let content = read_to_string(path).await.unwrap();
        let fortunes: Vec<String> = content
            .split("%")
            .filter_map(|x: &str| {
                if x.is_empty() {
                    None
                } else {
                    Some(x.to_string())
                }
            })
            .collect();
        info!("Read file {path}");
        Self { fortunes }
    }

    async fn handle(&self, res: &mut Response) {
        let mut rng = thread_rng();
        let x = rng.gen_range(0..self.fortunes.len());
        let ft = self.fortunes.get(x);
        if let Some(x) = ft {
            res.render(Text::Plain(x));
        } else {
            res.render(StatusCode::INTERNAL_SERVER_ERROR);
        }
    }
}

#[tokio::main]
async fn main() {
    let start = Instant::now();
    tracing_subscriber::fmt().init();

    let router = build_router().await;
    let acceptor = TcpListener::new("127.0.0.1:5800").bind().await;
    let server = Server::new(acceptor);
    let handler = server.handle();
    ctrlc::set_handler(move || {
        handler.stop_graceful(None);
    })
    .unwrap();
    info!("Start Server in {}ms!", start.elapsed().as_millis());
    server.serve(router).await;
}

async fn build_router() -> Router {
    let fh = Fortune::new("data/fortunes");
    let ch = Fortune::new("data/chinese");
    let (fh, ch) = join!(fh, ch);
    let forturn_route = Router::new().path("fortune").get(fh);
    let ch_route = Router::new().path("chinese").get(ch);
    let router = Router::new().get(hello).push(forturn_route).push(ch_route);
    router
}
